package main

import (
	"net/http"
	"time"

	"github.com/golang/glog"
	"github.com/gorilla/websocket"
)

const (
	writeWait = 8 * time.Second

	pongWait = 20 * time.Second

	pingPeriod = (pongWait * 9) / 10

	maxMessageSize = 1024 * 32
)

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024 * 32 * 4,
	WriteBufferSize: 1024 * 32 * 4,
	CheckOrigin:     func(r *http.Request) bool { return true },
}

type Client struct {
	hub *Hub

	conn *websocket.Conn

	send chan []byte

	id string
}

func (c *Client) readPump() {
	defer func() {
		c.hub.unregister <- c
		c.conn.Close()
	}()

	c.conn.SetReadLimit(maxMessageSize)
	c.conn.SetReadDeadline(time.Now().Add(pongWait))
	c.conn.SetPongHandler(func(string) error {
		c.conn.SetReadDeadline(time.Now().Add(pongWait))
		return nil
	})

	for {
		mtype, message, err := c.conn.ReadMessage()
		if err != nil {
			if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
				glog.Warningf("error:%v", err)
			}
			break
		}

		if mtype == websocket.TextMessage {
			c.hub.message <- Message{Client: c, MType: mtype, Data: message}
		}
	}
}

func (c *Client) writePump() {
	ticker := time.NewTicker(pingPeriod)
	defer func() {
		ticker.Stop()
		c.conn.Close()
	}()

	for {
		select {
		case message, ok := <-c.send:
			c.conn.SetWriteDeadline(time.Now().Add(writeWait))
			if !ok {
				c.conn.WriteMessage(websocket.CloseMessage, []byte{})
				return
			}

			if err := c.conn.WriteMessage(websocket.TextMessage, message); err != nil {
				glog.Warning(err)
				return
			}

			n := len(c.send)
			for i := 0; i < n; i++ {
				if err := c.conn.WriteMessage(websocket.TextMessage, <-c.send); err != nil {
					glog.Warning(err)
					return
				}
			}
		case <-ticker.C:
			c.conn.SetWriteDeadline(time.Now().Add(writeWait))
			if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil {
				return
			}
		}
	}
}

func serveWs(hub *Hub, w http.ResponseWriter, r *http.Request) {
	glog.Info("a client come in. serveWs")
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		glog.Info(err)
		return
	}

	client := &Client{hub: hub, conn: conn, send: make(chan []byte, 16)}
	client.hub.register <- client

	go client.writePump()
	go client.readPump()
}
